﻿using System;
using System.Collections.Generic;
using System.Web;
using NUnit.Framework;
using Rhino.Mocks;
using System.Data.SqlClient;
using System.Collections.Specialized;
using System.Collections;

namespace HttpLogger.Tests
{
    [TestFixture]
    public class LoggerTests
    {
        #region Extension Method Tests
        [Test]
        public void Can_Map_And_Filter_Specialized_Collections_To_Delimited_String() 
        {
            // Test what we can 

            string expUnfiltered = "Key1:Val1\nKey2:Val2\nKey3:Val3\nKey4:Val4";
            string expFiltered = "Key1:Val1\nKey3:Val3";
            string expCookieUnfiltered = "CookieKey1:CookieVal1\nCookieKey2:CookieVal2\nJunk:1923740912374012730";
            string expCookieFiltered = "CookieKey1:CookieVal1\nCookieKey2:CookieVal2";

            // General
            NameValueCollection nvc = new NameValueCollection(5);
            nvc.Add("Key1", "Val1");
            nvc.Add("Key2", "Val2");
            nvc.Add("Key3", "Val3");
            nvc.Add("Key4", "Val4");

            // cookie etc
            HttpCookieCollection cookie = new HttpCookieCollection();
            cookie.Add(new HttpCookie("CookieKey1", "CookieVal1"));
            cookie.Add(new HttpCookie("CookieKey2", "CookieVal2"));
            cookie.Add(new HttpCookie("Junk", "1923740912374012730"));

            Assert.AreEqual(expCookieUnfiltered, HttpLogger.Extensions.SpecialExtensions.SpecializedToString(cookie, ""));
            Assert.AreEqual(expCookieFiltered, HttpLogger.Extensions.SpecialExtensions.SpecializedToString(cookie, "Key.*"));
            Assert.AreEqual(expUnfiltered, HttpLogger.Extensions.SpecialExtensions.SpecializedToString(nvc, ""));
            Assert.AreEqual(expFiltered, HttpLogger.Extensions.SpecialExtensions.SpecializedToString(nvc, "Key[1,3]"));
        }
        #endregion

        #region SimpleLogger Tests
        [Test]
        public void Can_Skip_Filtered_ContentType()
        {
            SqlAuditLogger mockSqlLogger = MockRepository.GenerateMock<SqlAuditLogger>(MockRepository.GenerateStub<SqlConnectionFactory>(""));
            HttpApplication mockApp = MockRepository.GenerateMock<HttpApplication>();

            SimpleLogger loggerUnderTest = new SimpleLogger(mockSqlLogger, "media/.*", "", ""); // Not text/html shouldn't filter

            AuditLog logRecord = new AuditLog()
            {
                IP = "1.1.1.1",
                Url = "/Here.aspx",
                ResponseType = "200 OK",
                FormData = "UserName:value",
                CookieData = "cookieHere:I%27m+yummy!",
                HeaderData = "Status:200 OK",
                ServerData = "SCRIPT_NAME:/Here.aspx\nREMOTE_ADDR:1.1.1.1",
                ModulesData = "",
                RequestData = "/Here.aspx",
                SessionData = ""
            };

            Uri url = new Uri("http://awebsite.com/Here.aspx");
  
            var form = new NameValueCollection(1);
            form.Add("UserName", "value");
            
            var headers = new NameValueCollection(1);
            headers.Add("Status", "200 OK");
            
            var cookies = new HttpCookieCollection();
            cookies.Add(new HttpCookie("cookieHere", "I'm yummy!"));
            
            var serverVariables = new NameValueCollection(1);
            serverVariables.Add("SCRIPT_NAME", logRecord.Url);
            serverVariables.Add("REMOTE_ADDR", logRecord.IP);

            HttpModuleCollection modules = null;
            System.Web.SessionState.HttpSessionState session = null;

            loggerUnderTest.AuditVisit("text/html", url, logRecord.IP, form, serverVariables, session, headers, modules, cookies);

            mockSqlLogger.AssertWasNotCalled(f => f.LogRecord(Arg<AuditLog>.Is.Anything));
        }

        [Test]
        public void Can_Skip_Filtered_Extensions()
        {
            SqlAuditLogger mockSqlLogger = MockRepository.GenerateMock<SqlAuditLogger>(MockRepository.GenerateStub<SqlConnectionFactory>(""));
            HttpApplication mockApp = MockRepository.GenerateMock<HttpApplication>();

            SimpleLogger loggerUnderTest = new SimpleLogger(mockSqlLogger, "", "", ".*\\.asp$");// Not aspx shouldn't filter

            AuditLog logRecord = new AuditLog()
            {
                IP = "1.1.1.1",
                Url = "/Here.aspx",
                ResponseType = "200 OK",
                FormData = "UserName:value",
                CookieData = "cookieHere:I%27m+yummy!",
                HeaderData = "Status:200 OK",
                ServerData = "SCRIPT_NAME:/Here.aspx\nREMOTE_ADDR:1.1.1.1",
                ModulesData = "",
                RequestData = "/Here.aspx",
                SessionData = ""
            };

            Uri url = new Uri("http://awebsite.com/Here.aspx");

            var form = new NameValueCollection(1);
            form.Add("UserName", "value");

            var headers = new NameValueCollection(1);
            headers.Add("Status", "200 OK");

            var cookies = new HttpCookieCollection();
            cookies.Add(new HttpCookie("cookieHere", "I'm yummy!"));

            var serverVariables = new NameValueCollection(1);
            serverVariables.Add("SCRIPT_NAME", logRecord.Url);
            serverVariables.Add("REMOTE_ADDR", logRecord.IP);

            HttpModuleCollection modules = null;
            System.Web.SessionState.HttpSessionState session = null;


            loggerUnderTest.AuditVisit("text/html", url, logRecord.IP, form, serverVariables, session, headers, modules, cookies);

            mockSqlLogger.AssertWasNotCalled(f => f.LogRecord(Arg<AuditLog>.Is.Anything));
        }

        [Test]
        public void Can_Skip_Filtered_FormKeys()
        {
            SqlAuditLogger mockSqlLogger = MockRepository.GenerateMock<SqlAuditLogger>(MockRepository.GenerateStub<SqlConnectionFactory>(""));
            HttpApplication mockApp = MockRepository.GenerateMock<HttpApplication>();
            SimpleLogger loggerUnderTest = new SimpleLogger(mockSqlLogger, "", @"(?<!^CreditCard|^CVV|^ExpirationMonth|^ExpirationYear|^__VIEWSTATE)$", "");// Not aspx shouldn't filter

            // Form filter is under test
            string expectedFormData = @"OnlyThisKey:ShouldExist";

            Uri url = new Uri("http://awebsite.com/Here.aspx");

            var form = new NameValueCollection(1);
            form.Add("CreditCard", "42222");
            form.Add("CVV", "1234");
            form.Add("ExpirationMonth", "05");
            form.Add("ExpirationYear", "2013");
            form.Add("__VIEWSTATE", "?=paijsdfpoajsdfpojasopdfjaosdj");
            form.Add("OnlyThisKey", "ShouldExist");

            var headers = new NameValueCollection(1);
            headers.Add("Status", "200 OK");

            var cookies = new HttpCookieCollection();
            cookies.Add(new HttpCookie("cookieHere", "I'm yummy!"));

            var serverVariables = new NameValueCollection(1);
            serverVariables.Add("SCRIPT_NAME", "Here.aspx");
            serverVariables.Add("REMOTE_ADDR", "1.1.1.1");

            HttpModuleCollection modules = null;
            System.Web.SessionState.HttpSessionState session = null;

            loggerUnderTest.AuditVisit("text/html", url, "1.1.1.1", form, serverVariables, session, headers, modules, cookies);

            mockSqlLogger.AssertWasCalled(c => c.LogRecord(Arg<AuditLog>.Matches(
                l => l.FormData == expectedFormData
                )));
        }
 
        [Test]
        public void Can_Audit_Log_To_Database()
        {
            SqlAuditLogger mockSqlLogger = MockRepository.GenerateMock<SqlAuditLogger>(MockRepository.GenerateStub<SqlConnectionFactory>(""));
            HttpApplication mockApp = MockRepository.GenerateMock<HttpApplication>();
            SimpleLogger loggerUnderTest = new SimpleLogger(mockSqlLogger, "", "", "");

            AuditLog logRecord = new AuditLog()
            {
                IP = "1.1.1.1",
                Url = "/Here.aspx",
                ResponseType = "text/html",
                FormData = "UserName:value",
                CookieData = "cookieHere:I%27m+yummy!",
                HeaderData = "Status:200 OK",
                ServerData = "SCRIPT_NAME:/Here.aspx\nREMOTE_ADDR:1.1.1.1",
                ModulesData = "",
                RequestData = "/Here.aspx",
                SessionData = ""
            };

            Uri url = new Uri("http://awebsite.com/Here.aspx");
            
            var form = new NameValueCollection(1);
            form.Add("UserName", "value");
         
            var headers = new NameValueCollection(1);
            headers.Add("Status", "200 OK");
            
            var cookies = new HttpCookieCollection();
            cookies.Add(new HttpCookie("cookieHere", "I'm yummy!"));
            
            var serverVariables = new NameValueCollection(1);
            serverVariables.Add("SCRIPT_NAME", logRecord.Url);
            serverVariables.Add("REMOTE_ADDR", logRecord.IP);

            HttpModuleCollection modules = null;
            System.Web.SessionState.HttpSessionState session = null;

            loggerUnderTest.AuditVisit(logRecord.ResponseType, url, logRecord.IP, form, serverVariables, session, headers, modules, cookies);

            mockSqlLogger.AssertWasCalled(c => c.LogRecord(Arg<AuditLog>.Matches(
                l => l.IP == logRecord.IP
                    && l.ModulesData == logRecord.ModulesData
                    && l.RequestData == logRecord.RequestData
                    && l.SessionData == logRecord.SessionData
                    && l.ServerData == logRecord.ServerData
                    && l.CookieData == logRecord.CookieData
                    && l.HeaderData == logRecord.HeaderData
                    && l.Url == logRecord.Url
                    && l.FormData == logRecord.FormData
                    && l.ResponseType == logRecord.ResponseType
                )));
        }
        #endregion

        #region Sql AuditLogger Tests
        [Test]
        public void Can_Log_To_Sql_Database()
        {
            SqlConnectionFactory mockFactory = MockRepository.GenerateMock<SqlConnectionFactory>("");
            SqlConnection mockSql = MockRepository.GenerateMock<SqlConnection>();
            SqlCommand mockCmd = MockRepository.GenerateStub<SqlCommand>();
            SqlParameterCollection mockParam = MockRepository.GenerateStub<SqlParameterCollection>();

            mockFactory.Stub(x => x.CreateConnection()).Return(mockSql);
            mockSql.Stub(x => x.CreateCommand()).Return(mockCmd);
            mockCmd.Stub(x => x.ExecuteNonQuery()).Return(1);
            mockCmd.Stub(x => x.Parameters).Return(mockParam);
            mockParam.Stub(x => x.AddWithValue(Arg<string>.Is.Anything, Arg<object>.Is.Anything));

            SqlAuditLogger loggerToTest = new SqlAuditLogger(mockFactory);

            AuditLog logRecord = new AuditLog() { 
                IP = "1.1.1.1",
                Url = "/Here.aspx",
                ResponseType = "ResponseType",
                CookieData = "CookieData",
                HeaderData = "HeaderData",
                ServerData = "ServerData",
                FormData = "FormData",
                ModulesData = "ModulesData",
                RequestData = "RequestData",
                SessionData = "SessionData"
            };

            loggerToTest.LogRecord(logRecord);

            mockFactory.AssertWasCalled(c => c.CreateConnection());
            mockSql.AssertWasCalled(c => c.Open());
            mockSql.AssertWasCalled(c => c.CreateCommand());
            mockCmd.AssertWasCalled(c => c.ExecuteNonQuery());

            mockParam.AssertWasCalled(c => c.AddWithValue("@pIP", logRecord.IP));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pUrl", logRecord.Url));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pContentType", logRecord.ResponseType));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pCookieData", logRecord.CookieData));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pHeaderData", logRecord.HeaderData));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pServerData", logRecord.ServerData));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pFormData", logRecord.FormData));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pModulesData", logRecord.ModulesData));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pRequestData", logRecord.RequestData));
            mockParam.AssertWasCalled(c => c.AddWithValue("@pSessionData", logRecord.SessionData));
        }
        #endregion
    }
}
